package com.xhwl.logistics.strategy.impl;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.xhwl.common.exception.BusinessException;
import com.xhwl.logistics.bo.VehicleCarportBO;
import com.xhwl.logistics.bo.VehicleParkingLotSecondBO;
import com.xhwl.logistics.bo.cent.VehicleIntegratedConfigBO;
import com.xhwl.logistics.bo.input.QueryVehicleCarportBO;
import com.xhwl.logistics.bo.manipulate.VehicleSyncBO;
import com.xhwl.logistics.constant.VehicleConstant;
import com.xhwl.logistics.converter.mapstruct.CentBiConverter;
import com.xhwl.logistics.converter.mapstruct.VehicleAccessRecordConverter;
import com.xhwl.logistics.converter.mapstruct.VehicleCarportConverter;
import com.xhwl.logistics.converter.mapstruct.VehiclePayRecordConverter;
import com.xhwl.logistics.converter.mapstruct.VehicleSyncConverter;
import com.xhwl.logistics.dao.mapper.IVehicleAreaMapper;
import com.xhwl.logistics.dao.mapper.IVehicleParkingLotMapper;
import com.xhwl.logistics.dao.mapper.IVehiclePassageMapper;
import com.xhwl.logistics.dto.http.CarportHaiKangDTO;
import com.xhwl.logistics.dto.http.ParkingLotInfoHaiKangDTO;
import com.xhwl.logistics.dto.http.RoadwayHaiKangDTO;
import com.xhwl.logistics.dto.http.query.QueryCarportHaiKangDTO;
import com.xhwl.logistics.entity.VehicleArea;
import com.xhwl.logistics.entity.VehicleParkingLot;
import com.xhwl.logistics.entity.VehiclePassage;
import com.xhwl.logistics.service.IVehicleAccessRecordService;
import com.xhwl.logistics.service.IVehicleParkingLotService;
import com.xhwl.logistics.service.IVehiclePayRecordService;
import com.xhwl.logistics.service.cent.IBIVehicleService;
import com.xhwl.logistics.service.impl.http.HaiKangHttpServiceImpl;
import com.xhwl.logistics.strategy.IVehicleParkingLotHandler;
import com.xhwl.logistics.util.AboutDateUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
//@Service
//（1）该同步逻辑，遍历了多次集合，虽然数据量不大，后续可以优化，一次遍历把所有逻辑结果赋值成功
//（2）同步车场，同步区域，同步通道，可以分别抽取出方法进行封装
//（3）集合筛选和转变可以考虑放在一起（后续优化）
public class VehicleParkingLotHaiKangHandlerImpl implements IVehicleParkingLotHandler {

    @Autowired
    private HaiKangHttpServiceImpl haiKangHttpService;

    @Autowired
    private IVehicleParkingLotMapper vehicleParkingLotMapper;

    @Autowired
    private IVehicleAreaMapper vehicleAreaMapper;

    @Autowired
    private IVehiclePassageMapper vehiclePassageMapper;

    @Autowired
    private IVehicleParkingLotService vehicleParkingLotService;

    @Autowired
    private IVehicleAccessRecordService vehicleAccessRecordService;

    @Autowired
    private IVehiclePayRecordService vehiclePayRecordService;

    @Autowired
    private IBIVehicleService biVehicleService;

    @Autowired
    private VehicleSyncConverter vehicleSyncConverter;

    @Autowired
    private VehicleCarportConverter vehicleCarportConverter;

    @Autowired
    private VehicleAccessRecordConverter vehicleAccessRecordConverter;

    @Autowired
    private VehiclePayRecordConverter vehiclePayRecordConverter;

    @Autowired
    private CentBiConverter centBiConverter;

    private Wrapper<VehicleParkingLot> conditionForJudgeExistParkingLot(Integer projectId) {
        return null;
        /*return new LambdaQueryWrapper<>(VehicleParkingLot.class)
                .eq(VehicleParkingLot::getProjectId, projectId)
                .last("limit 1");*/
    }

    private Wrapper<VehicleParkingLot> conditionForQueryParkingLot(Integer projectId, String parkingLotName) {
        return null;
        /*return new LambdaQueryWrapper<>(VehicleParkingLot.class)
                .eq(VehicleParkingLot::getProjectId, projectId)
                .eq(VehicleParkingLot::getParkingLotName, parkingLotName);*/
    }

    @Override
    @Transactional
    public void sync(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, VehicleSyncBO vehicleSyncBO) {
        log.info("HaiKang Sync Start!");
        log.info("VehicleIntegratedConfigBO: " + vehicleIntegratedConfigBO);

        Integer projectId = vehicleIntegratedConfigBO.getVehicleSystemInfoBO().getProjectId();

        if ( !vehicleParkingLotMapper.selectList(conditionForJudgeExistParkingLot(projectId)).isEmpty() ) {
            log.info("HaiKang Sync Already!");
            return;
        }

        log.info("====================ParkingLot=========================");
        List<ParkingLotInfoHaiKangDTO> parkingLotInfoHaiKangDTOList = haiKangHttpService.getParkingLotInfo(vehicleIntegratedConfigBO.getVehicleParkingInfoBO());
        //从海康getParkingLotInfo返回结果筛选出车场
        List<VehicleParkingLot> vehicleParkingLots = parkingLotInfoHaiKangDTOList.stream()
                .filter(parkingLotInfoHaiKangDTO -> "0".equals(parkingLotInfoHaiKangDTO.getParentParkIndexCode()))
                .map(parkingLotInfoHaiKangDTO -> vehicleSyncConverter.toVehicleParkingLot(parkingLotInfoHaiKangDTO, projectId))
                .collect(Collectors.toList());

        List<CarportHaiKangDTO> carportHaiKangDTOList = haiKangHttpService.getCarport(vehicleIntegratedConfigBO.getVehicleParkingInfoBO());
        //对车场集合，各个车场设置相应的车位总数
        vehicleParkingLots.forEach(vehicleParkingLot -> {
            String parkId = vehicleParkingLot.getParkId();
            CarportHaiKangDTO carport = carportHaiKangDTOList.stream()
                    .filter(carportHaiKangDTO -> parkId.equals(carportHaiKangDTO.getParkSyscode()))
                    .findAny()
                    .orElseThrow(() -> new IllegalStateException("根据车场找不到相对应的车位总数"));
            vehicleParkingLot.setCarportTotalNumber(carport.getTotalPlace());
        });

        //循环insert车场到数据库
        vehicleParkingLots.forEach(vehicleParkingLot -> {
            vehicleParkingLotMapper.insert(vehicleParkingLot);
        });



        log.info("====================Area======================");
        //从海康getParkingLotInfo返回结果筛选出车场区域，通过其所属车场来筛选
        List<ParkingLotInfoHaiKangDTO> parkingLotInfoHaiKangDTOListForArea = parkingLotInfoHaiKangDTOList.stream().filter(parkingLotInfoHaiKangDTO -> {
            String parentParkIndexCode = parkingLotInfoHaiKangDTO.getParentParkIndexCode();
            return vehicleParkingLots.stream()
                    .anyMatch(vehicleParkingLot -> parentParkIndexCode.equals(vehicleParkingLot.getParkId()));
        }).collect(Collectors.toList());

        //把区域和相应的新增后车场的主键id关联
        List<VehicleArea> vehicleAreaList = parkingLotInfoHaiKangDTOListForArea.stream().map(parkingLotInfoHaiKangDTO -> {
            String parentParkIndexCode = parkingLotInfoHaiKangDTO.getParentParkIndexCode();
            VehicleParkingLot parkingLot = vehicleParkingLots.stream()
                    .filter(vehicleParkingLot -> parentParkIndexCode.equals(vehicleParkingLot.getParkId()))
                    .findAny()
                    .orElseThrow(() -> (new IllegalStateException("区域找不到相对应的车场")));
            return vehicleSyncConverter.toVehicleArea(parkingLotInfoHaiKangDTO, parkingLot.getId(), parkingLot.getCarportTotalNumber());
        }).collect(Collectors.toList());

        //循环insert区域到数据库
        vehicleAreaList.forEach(vehicleArea -> {
            vehicleAreaMapper.insert(vehicleArea);
        });



        log.info("===================Passage======================");
        //从海康getParkingLotInfo返回结果筛选出车道，通过其所属区域来筛选
        List<ParkingLotInfoHaiKangDTO> parkingLotInfoHaiKangDTOListForPassage = parkingLotInfoHaiKangDTOList.stream().filter(parkingLotInfoHaiKangDTO -> {
            String parentParkIndexCode = parkingLotInfoHaiKangDTO.getParentParkIndexCode();
            return parkingLotInfoHaiKangDTOListForArea.stream()
                    .anyMatch(area -> parentParkIndexCode.equals(area.getParkIndexCode()));
        }).collect(Collectors.toList());

        //调用海康的接口，获取所有车道的信息，（其中需要车道的类型：出口或入口）
        List<RoadwayHaiKangDTO> roadwayHaiKangDTOList = haiKangHttpService.getRoadways(vehicleIntegratedConfigBO.getVehicleParkingInfoBO(), vehicleSyncConverter.toEntranceIndexCodesString(parkingLotInfoHaiKangDTOListForArea));

        //把车道和相应的新增后区域的主键id关联,和海康接口返回通道信息（通道类型）关联
        List<VehiclePassage> vehiclePassageList = parkingLotInfoHaiKangDTOListForPassage.stream().map(parkingLotInfoHaiKangDTO -> {
            String parentParkIndexCode = parkingLotInfoHaiKangDTO.getParentParkIndexCode();
            VehicleArea area = vehicleAreaList.stream()
                    .filter(vehicleArea -> parentParkIndexCode.equals(vehicleArea.getIndexCode()))
                    .findAny().orElseThrow(() -> (new IllegalStateException("车场通道找不到相对应的区域")));

            String parkIndexCode = parkingLotInfoHaiKangDTO.getParkIndexCode();
            RoadwayHaiKangDTO roadway = roadwayHaiKangDTOList.stream()
                    .filter(roadwayHaiKangDTO -> parkIndexCode.equals(roadwayHaiKangDTO.getRoadwayIndexCode()))
                    .findAny().orElseThrow(() -> (new IllegalStateException("车场通道找不到相对应的海康接口返回的通道信息")));
            return vehicleSyncConverter.toVehiclePassage(parkingLotInfoHaiKangDTO, area.getId(), roadway.getRoadwayType());
        }).collect(Collectors.toList());

        //循环insert车道到数据库
        vehiclePassageList.forEach(vehiclePassage -> {
            vehiclePassageMapper.insert(vehiclePassage);
        });

        log.info("HaiKang Sync End!");
    }

    @Override
    public VehicleCarportBO queryCarport(VehicleIntegratedConfigBO vehicleIntegratedConfigBO, QueryVehicleCarportBO queryVehicleCarportBO) {

        Integer projectId = queryVehicleCarportBO.getProjectId();
        String parkingLotName = queryVehicleCarportBO.getParkingLotName();
        List<VehicleParkingLotSecondBO> vehicleParkingLotSecondBOList = vehicleParkingLotService.queryParkingLots(projectId, parkingLotName);
        if (vehicleParkingLotSecondBOList.isEmpty()) {
            throw new BusinessException("通过项目id和车场名称找不到车场");
        }

        List<VehicleCarportBO> vehicleCarportBOList = vehicleParkingLotSecondBOList.stream()
                .map(vehicleParkingLotSecondBO -> {
                    CarportHaiKangDTO carportHaiKangDTO = haiKangHttpService.getCarport(vehicleIntegratedConfigBO.getVehicleParkingInfoBO(), new QueryCarportHaiKangDTO(vehicleParkingLotSecondBO.getParkId()));
                    return vehicleCarportConverter.carportHaiKangDTO2VehicleCarportBO(carportHaiKangDTO);
                })
                .collect(Collectors.toList());

        return vehicleCarportBOList.stream()
                .reduce(new VehicleCarportBO(0, 0), (x, y) -> new VehicleCarportBO(Integer.sum(x.getTotalNumber(), y.getTotalNumber()), Integer.sum(x.getFreeNumber(), y.getFreeNumber())));
    }

    @Async("threadPoolTaskExecutorForSchedule")
    @Override
    public void statisticsToBi(VehicleIntegratedConfigBO vehicleIntegratedConfigBO) {
        Date processDate = new Date();
        Integer projectId = vehicleIntegratedConfigBO.getVehicleSystemInfoBO().getProjectId();

        List<VehicleParkingLotSecondBO> vehicleParkingLotSecondBOList = vehicleParkingLotService.queryParkingLots(projectId, null);
        vehicleParkingLotSecondBOList.forEach(vehicleParkingLotSecondBO -> {
            String parkId = vehicleParkingLotSecondBO.getParkId();
            String parkingLotName = vehicleParkingLotSecondBO.getParkingLotName();

            Date ingestionDate = AboutDateUtil.dateMinusHour(AboutDateUtil.theSameDayHourOfDate(processDate, VehicleConstant.vehicleTimeZone), 1);
            Integer enterForCount = vehicleAccessRecordService.accessRecordForCount(vehicleAccessRecordConverter.toQueryVehicleAccessRecordForCountBOForEnter(projectId, parkId, ingestionDate));
            Integer goOutForCount = vehicleAccessRecordService.accessRecordForCount(vehicleAccessRecordConverter.toQueryVehicleAccessRecordForCountBOForGoOut(projectId, parkId, ingestionDate));
            Integer totalForCount = enterForCount + goOutForCount;
            biVehicleService.addDwsAccessRecordForHour(centBiConverter.toCreateBiVehicleAccessRecordHourFeignDTO(projectId, parkingLotName, totalForCount, enterForCount, goOutForCount, processDate, ingestionDate));

            Integer payTotalForSum = vehiclePayRecordService.payRecordForCount(vehiclePayRecordConverter.toQueryVehiclePayRecordForCountBO(projectId, parkId, ingestionDate));
            biVehicleService.addDwsPayRecordForHour(centBiConverter.toCreateBiVehiclePayRecordHourFeignDTO(projectId, parkingLotName, payTotalForSum, 0, 0, 0, 0, processDate, ingestionDate));
        });
    }
}
